﻿using System.Collections.Generic;
using PhiStore.ObjectManaging;
using PhiStore.Packages;
using PhiStore.Packages.InternalPackages;
using PhiStore.Policies;
using PhiStore.Threading;

namespace PhiStore.Communication
{
    partial class Worker
    {
        private void NegotiationFinished(NegotiationCommit neg)
        {
            //forward to objectmanager
            currentObjectName = neg.Key;

            PhiServer.Instance.ObjectManagerChannel.Send(new NegotiationWinner(ExternalPeer, neg, fromObjectManager));
            ReturnCode result;
            ObjectLock oe = (ObjectLock)fromObjectManager.Receive();
            if (oe.Device.Exist(neg.Key))
            {
                result = oe.Device.UpdateObjectInfo(neg.Key, oe.ObjectInformation, false);
            }
            else
            {
                result = oe.Device.CreateObject(neg.Key, oe.Policy, oe.ObjectInformation, false);
            }
            
            ReleaseLockEntry();
            SendReply(result);
        }

        private void Negotiation(NegotiationInit neg)
        {
            //forward to objectmanager
            PhiServer.Instance.ObjectManagerChannel.Send(new ExternalNegotiation(neg, fromObjectManager));
            SendReply(fromObjectManager.Receive());
        }

        private ReturnCode Negotiate(string key, ulong bullynumber, Policy policy, long count, ulong thisVersion)
        {
            ReturnCode result = ReturnCode.Success;

            IList<PeerContainer> peers = PeerManager.Instance.GetPeers(); //all peers
            Channel<object> fromPeers = new Channel<object>(1); //receiving replies from peers

            SendReply(ReturnCode.WorkingOnObject); //keep alive

            PeerContainer bestVersionPeer = null;
            ulong bestVersion = thisVersion;
            List<PeerContainer> replicatingPeers = new List<PeerContainer>();
            foreach (PeerContainer peer in peers)
            {
                try
                {
                    //send request to peers
                    peer.PeerWorkerChannel.Send(
                        new ForwardedPackage(new NegotiationInit(key, bullynumber), fromPeers)
                        );

                    SendReply(ReturnCode.WorkingOnObject); //keep client alive

                    //receive reply from peers
                    NegotiationReply temp = (NegotiationReply)fromPeers.Receive();
                    result |= temp.Code;

                    if (temp.Version > bestVersion) //this peer has latest version
                    {
                        bestVersionPeer = peer;
                        bestVersion = temp.Version;
                    }
                    if (temp.Version > 0L)
                    {
                        replicatingPeers.Add(peer);
                    }
                }
                catch (PoisonException)
                {
                    PeerManager.Instance.RemovePeer(peer);
                }

            }

            if (result == ReturnCode.Success) //I won
            {
                PhiServer.Instance.ObjectManagerChannel.Send(new NegotiationSuccess(key, fromObjectManager));

                SendReply(ReturnCode.WorkingOnObject); //keep client alive
                foreach (PeerContainer peer in peers)
                {
                    try
                    {
                        peer.PeerWorkerChannel.Send(
                            new ForwardedPackage(new NegotiationCommit(key, policy, count), null)
                        );
                    }
                    catch (PoisonException)
                    {
                        PeerManager.Instance.RemovePeer(peer);
                    }
                }

                objectLock = (ObjectLock)fromObjectManager.Receive();

                SendReply(ReturnCode.WorkingOnObject); //keep client alive

                if (bestVersionPeer != null) //this peer doesn't have the latest version
                {
                    TryFetchCopyFromPeer(bestVersionPeer);
                }
                objectLock.ReplicatingPeers = replicatingPeers;
              
            }
            return result;
        }
    }
}
